package core;

/**
 * A class representing the Processor Status Register (PSR)
 * The registers can be set or read via the corresponding "set" or "get methods
 * Available registers:
 * 	Z: Set when the result of an arithmetical OR logical operation is zero
 * 	N: Set when the result of an arithmetical operation is a negative number
 * 	C: Set when the result of an arithmetical operation OR a shift operation
 * 		cannot be represented in 32bits. You can think of the C flag as the 
 * 		33rd bit of the result.
 *  V: Signed Overflow. When the 31rd carry bit != 32rd carry bit
 *  EI: Enable Interrupts
 *  TIM: Timer Interrupt Masked
 *  HIM: High priority Interrupt Masked
 *  LIM: Low priority Interrupt Masked
 *
 * @author Ximo Tur ximotur at gmail dot com
 */
public class PsrClass{
	private short flags = 0;
	private short fmods = 0;
	static private int EImask = 128;
	static private int LIMmask = 64;
	static private int HIMmask = 32;
	static private int TIMmask = 16;
	static private int Vmask = 8;
	static private int Nmask = 4;
	static private int Zmask = 2;
	static private int Cmask = 1;

	static private int EIbit = 8;
	static private int LIMbit = 7;
	static private int HIMbit = 6;
	static private int TIMbit = 5;
	static private int Vbit = 4;
	static private int Nbit = 3;
	static private int Zbit = 2;
	static private int Cbit = 1;

	/**
	 * 
	 * @return the combined value of the PSR flags
	 */
	public short getFlags(){
		return(flags);
	}

	/**
	 * 
	 * @return the value of the V flag of the PSR
	 */
	public int getV(){
		return(int)((flags & Vmask)>>(Vbit - 1));
	}
	/**
	 * 
	 * @return the value of the Z flag of the PSR
	 */
	public int getZ(){
		return(int)((flags & Zmask)>>(Zbit - 1));
	}
	/**
	 * 
	 * @return the value of the N flag of the PSR
	 */
	public int getN(){
		return(int)((flags & Nmask)>>(Nbit - 1));
	}
	/**
	 * 
	 * @return the value of the C flag of the PSR
	 */
	public int getC(){
		return(int)((flags & Cmask)>>(Cbit - 1));
	}
	/**
	 * 
	 * @return the value of the TIM flag of the PSR
	 */
	public int getTIM(){
		return(int)((flags & TIMmask)>>(TIMbit - 1));
	}
	/**
	 * 
	 * @return the value of the HIM flag of the PSR
	 */
	public int getHIM(){
		return(int)((flags & HIMmask)>>(HIMbit - 1));
	}
	/**
	 * 
	 * @return the value of the LIM flag of the PSR
	 */
	public int getLIM(){
		return(int)((flags & LIMmask)>>(LIMbit - 1));
	}
	/**
	 * 
	 * @return the value of the EI flag of the PSR
	 */
	public int getEI(){
		return(int)((flags & EImask)>>(EIbit - 1));
	}

	//TODO: change all the set methods to "protected"
	/**
	 * Sets the V (overflow flag
	 * @param v the value (1 or 0) to be set in the V flag
	 */
	public void setV(int v){
		if(v != this.getV()){
			this.fmods = (short)(fmods | Vmask);
		}
		flags = (short)((flags & ~Vmask) | ((v & 1)<<(Vbit-1)));
	}
	/**
	 * Sets the Z (zero or equal to) flag
	 * @param v the value (1 or 0) to be set in the Z flag
	 */
	public void setZ(int v){
		if(v != this.getZ()){
			this.fmods = (short)(fmods | Zmask);
		}
		flags = (short)((flags & ~Zmask) | ((v & 1)<<(Zbit-1)));
	}
	/**
	 * Sets the N (negative or less than) flag
	 * @param v the value (1 or 0) to be set in the N flag
	 */
	public void setN(int v){
		if(v != this.getN()){
			this.fmods = (short)(fmods | Nmask);
		}
		flags = (short)((flags & ~Nmask) | ((v & 1)<<(Nbit-1)));
	}
	/**
	 * Sets the C (Carry or borrow or extend) flag
	 * @param v the value (1 or 0) to be set in the C flag
	 */
	public void setC(int v){
		if(v != this.getC()){
			this.fmods = (short)(fmods | Cmask);
		}
		flags = (short)((flags & ~Cmask) | ((v & 1)<<(Cbit -1)));
	}
	/**
	 * Sets the TIM (Timer Interrupt Mask) flag
	 * @param v the value (1 or 0) to be set in the C flag
	 */
	public void setTIM(int v){
		if(v != this.getTIM()){
			this.fmods = (short)(fmods | TIMmask);
		}
		flags = (short)((flags & ~TIMmask) | ((v & 1)<<(TIMbit -1)));
	}
	/**
	 * Sets the HIM (High priority Interrupt Mask) flag
	 * @param v the value (1 or 0) to be set in the HIM flag
	 */
	public void setHIM(int v){
		if(v != this.getHIM()){
			this.fmods = (short)(fmods | HIMmask);
		}
		flags = (short)((flags & ~HIMmask) | ((v & 1)<<(HIMbit -1)));
	}
	/**
	 * Sets the LIM (Low priority Interrupt Mask) flag
	 * @param v the value (1 or 0) to be set in the LIM flag
	 */
	public void setLIM(int v){
		if(v != this.getLIM()){
			this.fmods = (short)(fmods | LIMmask);
		}
		flags = (short)((flags & ~LIMmask) | ((v & 1)<<(LIMbit -1)));
	}
	/**
	 * Sets the EI (Enable Interrupts) flag
	 * @param v the value (1 or 0) to be set in the EI flag
	 */
	public void setEI(int v){
		if(v != this.getEI()){
			this.fmods = (short)(fmods | EImask);
		}
		flags = (short)((flags & ~EImask) | ((v & 1)<<(EIbit -1)));
	}
	/**
	 * Sets the entire Flags value.
	 * @param v the combined value to be set as Flags. This value will be AND whith 7.
	 */
	public void setFlags(int v){
		flags = (short)(v & (Cmask | Zmask | Nmask | Vmask | TIMmask | HIMmask | LIMmask | EImask));
	}

	/**
	 * Clears the previous modifications done on the PSR.
	 */
	public void clearMods(){
		this.fmods = 0;
	}
	/**
	 * States whether the V value changed in the last execution
	 * @return
	 */
	public boolean getVChanged(){
		return(((fmods & Vmask)>>(Vbit - 1)) == 1);
	}
	/**
	 * States whether the Z value changed in the last execution
	 * @return
	 */
	public boolean getZChanged(){
		return(((fmods & Zmask)>>(Zbit - 1)) == 1);
	}
	/**
	 * States whether the N value changed in the last execution
	 * @return
	 */
	public boolean getNChanged(){
		return(((fmods & Nmask)>>(Nbit - 1)) == 1);
	}
	/**
	 * States whether the C value changed in the last execution
	 * @return
	 */
	public boolean getCChanged(){
		return(((fmods & Cmask)>>(Cbit - 1)) == 1);
	}
	/**
	 * States whether the TIM value changed in the last execution
	 * @return
	 */
	public boolean getTIMChanged(){
		return(((fmods & TIMmask)>>(TIMbit - 1)) == 1);
	}
	/**
	 * States whether the HIM value changed in the last execution
	 * @return
	 */
	public boolean getHIMChanged(){
		return(((fmods & HIMmask)>>(HIMbit - 1)) == 1);
	}
	/**
	 * States whether the LIM value changed in the last execution
	 * @return
	 */
	public boolean getLIMChanged(){
		return(((fmods & LIMmask)>>(LIMbit - 1)) == 1);
	}
	/**
	 * States whether the EI value changed in the last execution
	 * @return
	 */
	public boolean getEIChanged(){
		return(((fmods & EImask)>>(EIbit - 1)) == 1);
	}
	/**
	 * Just a test purposes method
	 */
	public void printValues(){
		System.out.println("======= PSR Values ======");
		System.out.print("V value: " + this.getV());
		System.out.print("\tN value: " + this.getN());
		System.out.print("\tZ value: " + this.getZ());
		System.out.print("\tC value: " + this.getC());
		System.out.print("\tTIM value: " + this.getTIM());
		System.out.print("\tHIM value: " + this.getHIM());
		System.out.print("\tLIM value: " + this.getLIM());
		System.out.print("\tEI value: " + this.getEI());
		System.out.println("\tFlags value: " + this.getFlags());
		System.out.println("======= End PSR Values ======");
	}
}
